/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    crg.c
 *  @brief   CRG source file
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

#include "crg.h"
#include "syscounter.h"
#include "autoconf.h"
//#define CONFIG_EVB_BOARD 1
typedef struct crg_mod_clk_freq crg_mod_clk_freq_t;

struct crg_mod_clk_freq {
	enum crg_mod mod;
	clk_freq     freq;
};

#if defined(CONFIG_EVB_BOARD)
#define DEF_MOD_CLK_FREQ     CLK_FREQ_25MHZ

static crg_mod_clk_freq_t g_sys_crg_mod_clk_freq[] =
{
	{ CRG_MOD_CPU_CORE, CLK_FREQ_200MHZ },
	{ CRG_MOD_SYSCNT,   CLK_FREQ_25MHZ  },
	{ CRG_MOD_TIMER0,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER1,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER2,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER3,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER4,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER5,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER6,   CLK_FREQ_100MHZ },
	{ CRG_MOD_TIMER7,   CLK_FREQ_100MHZ },
	{ CRG_MOD_WDT0,     CLK_FREQ_32KHZ  },
	{ CRG_MOD_WDT1,     CLK_FREQ_32KHZ  },
	{ CRG_MOD_TSENSOR,  CLK_FREQ_500KHZ },
	{ CRG_MOD_PMU,      CLK_FREQ_32KHZ  },
	{ CRG_MOD_RTC,      CLK_FREQ_32KHZ  },

	{ CRG_MOD_UART0,    CLK_FREQ_100MHZ  },
	{ CRG_MOD_UART1,    CLK_FREQ_100MHZ  },
	{ CRG_MOD_UART2,    CLK_FREQ_100MHZ  },
	{ CRG_MOD_UART3,    CLK_FREQ_100MHZ  },
	{ CRG_MOD_UART4,    CLK_FREQ_100MHZ  },
	{ CRG_MOD_I2C0,     CLK_FREQ_100MHZ  },
	{ CRG_MOD_I2C1,     CLK_FREQ_100MHZ  },
	{ CRG_MOD_SPI0,     CLK_FREQ_100MHZ  },
	{ CRG_MOD_SPI1,     CLK_FREQ_100MHZ  },
	{ CRG_MOD_SPI2,     CLK_FREQ_100MHZ  },
	{ CRG_MOD_SPI3,     CLK_FREQ_50MHZ   },
};
#else
#define DEF_MOD_CLK_FREQ     5006223

static crg_mod_clk_freq_t g_sys_crg_mod_clk_freq[] =
{
	{ CRG_MOD_CPU_CORE, 40049780    },
	{ CRG_MOD_SYSCNT,   5006223     },
	{ CRG_MOD_TIMER0,   20024890    },
	{ CRG_MOD_TIMER1,   20024890    },
	{ CRG_MOD_TIMER2,   20024890    },
	{ CRG_MOD_TIMER3,   20024890    },
	{ CRG_MOD_TIMER4,   20024890    },
	{ CRG_MOD_TIMER5,   20024890    },
	{ CRG_MOD_TIMER6,   20024890    },
	{ CRG_MOD_TIMER7,   20024890    },
	{ CRG_MOD_WDT0,     750         },
	{ CRG_MOD_WDT1,     750         },
	{ CRG_MOD_TSENSOR,  5006223     },
	{ CRG_MOD_PMU,      48000       },
	{ CRG_MOD_RTC,      48000       },

	{ CRG_MOD_UART0,    20024890    },
	{ CRG_MOD_UART1,    20024890    },
	{ CRG_MOD_UART2,    20024890    },
	{ CRG_MOD_UART3,    20024890    },
	{ CRG_MOD_UART4,    20024890    },
	{ CRG_MOD_I2C0,     20024890    },
	{ CRG_MOD_I2C1,     20024890    },
	{ CRG_MOD_SPI0,     20024890    },
	{ CRG_MOD_SPI1,     20024890    },
	{ CRG_MOD_SPI2,     20024890    },
	//{ CRG_MOD_SPI3,     20024890    },
	{ CRG_MOD_SPI3,     10012445    },
};
#endif

#define MOD_CLK_FREQ_PTR(i)  (&g_sys_crg_mod_clk_freq[i])

void pll_sd_adc_reset(void)
{
	CRG_REG_PTR()->reset |= LOCAL_SD_ADC_RESET;
}

void pll_sar_adc_reset(void)
{
	CRG_REG_PTR()->reset |= LOCAL_SAR_ADC_RESET;
}

bool is_pll_locked(void)
{
	return ((CRG_REG_PTR()->pllsta & SOC_PLL_LOCK_STA) == 0) ? 0 : 1;
}

void global_reset(void)
{
	CRG_REG_PTR()->glb_reset |= LOCAL_GLOBAL_RESET;
}

uint32_t get_crg_wdt_rst_counter(void)
{
	return GET_REG_BITS(CRG_REG_PTR()->rst_count, WDT_RST_COUNTER_POS, WDT_RST_COUNTER_LEN);
}

uint32_t get_crg_lockup_rst_counter(void)
{
	return GET_REG_BITS(CRG_REG_PTR()->rst_count, LOCKUP_RST_COUNTER_POS, LOCKUP_RST_COUNTER_LEN);
}

uint32_t pll_clk_alm_sta(void)
{
	return GET_REG_BITS(CRG_REG_PTR()->clk_alm_monitor, CLK_ARLARM_POS, CLK_ARLARM_LEN);
}

static void pll_cfg_output(void)
{
	uint32_t val = 0;
	// (24 / 3) * 2 *(49 + 0.15) = 786.4
	val = SET_REG_BITS(val, SOC_PLL_N_POS, SOC_PLL_N_LEN, 0x31);
	val = SET_REG_BITS(val, SOC_PLL_M_POS, SOC_PLL_M_LEN, 0x01);
	val = SET_REG_BITS(val, SOC_PLL_PREDIV_POS, SOC_PLL_PREDIV_LEN, 0x03);
	CRG_REG_PTR()->soc_pll0 = val;
	// frac is 0.15
	val = CRG_REG_PTR()->soc_pll1;
	val = SET_REG_BITS(val, SOC_PLL1_FRADIV_POS, SOC_PLL1_FRADIV_LEN, 0x26e97);
	CRG_REG_PTR()->soc_pll1 = val;

	//786.4 / 4 = 196.6
	val = CRG_REG_PTR()->soc_pll3;
	val = SET_REG_BITS(val, SOC_PLL3_POSTDIV_POS, SOC_PLL3_POSTDIV_LEN, 0x01);
	CRG_REG_PTR()->soc_pll3 = val;
}

static void pll_cfg_core_adc_div(void)
{
	uint32_t val = 0;

	/* core, 200MHz */
	val = CRG_REG_PTR()->clkdiv;
	//val = SET_REG_BITS(val, PLL_CPU_CLK_DIV_POS, PLL_CPU_CLK_DIV_LEN, 0x01);
	/* APB bus, 100MHz */
	//val = SET_REG_BITS(val, PLL_APB_CLK_DIV_POS, PLL_APB_CLK_DIV_LEN, 0x02);
	/* SD ADC, 20MHz / 24 = 833KHz */
	//val = SET_REG_BITS(val, PLL_SD_CLK_DIV_POS, PLL_SD_CLK_DIV_LEN, 0x18);
	/* SAR ADC, 20MHz */
	//val = SET_REG_BITS(val, PLL_SAR_CLK_DIV_POS, PLL_SAR_CLK_DIV_LEN, 0x01);
	// SD ADC default sample rate is 6.4K
	val = SET_REG_BITS(val, PLL_SD_CLK_DIV_POS, PLL_SD_CLK_DIV_LEN, 0x0C);

	CRG_REG_PTR()->clkdiv = val;
}

static void pll_cfg_bus_div(void)
{
	uint32_t val = 0;

	/* AHB bus, 100MHz */
	val = SET_REG_BITS(val, PLL_BUS_HCLK_DIV_POS, PLL_BUS_HCLK_DIV_LEN, 0x02);
	/* HAC 20MHz */
	val = SET_REG_BITS(val, PLL_HAC_CLK_DIV_POS, PLL_HAC_CLK_DIV_LEN, 0x0a);
	/* timer 100MHz */
	val = SET_REG_BITS(val, PLL_TIMER_CLK_DIV_POS, PLL_TIMER_CLK_DIV_LEN, 0x02);
	/* sensor 500K */
	val = SET_REG_BITS(val, PLL_SENSOR_CLK_DIV_POS, PLL_SENSOR_CLK_DIV_LEN, 0x32);

	CRG_REG_PTR()->busclkdiv = val;
}

void pll_select(enum pll_sel sel)
{
	uint32_t val = CRG_REG_PTR()->soc_pll2;

	val = SET_REG_BITS(val, SOC_PLL_SEL_POS, SOC_PLL_SEL_LEN, sel);

	CRG_REG_PTR()->soc_pll2 = val;
}

void switch_select(enum switch_sel sel)
{
	uint32_t val = CRG_REG_PTR()->soc_pll2;

	val = SET_REG_BITS(val, SOC_SWITCH_SEL_POS, SOC_SWITCH_SEL_LEN, sel);

	CRG_REG_PTR()->soc_pll2 = val;
}

void pll_clk_mon(enum clk_mon mon)
{
	uint32_t val = CRG_REG_PTR()->clk_monitor;

	val = SET_REG_BITS(val, CLK_MON_MUX_POS, CLK_MON_MUX_LEN, mon);

	CRG_REG_PTR()->clk_monitor = val;
}

static void clk_select(void)
{
	bool locked;
	u32 cnt = 0;

	do {
		locked = is_pll_locked();
		cnt++;
	} while (!locked && cnt < 1000);

	if (!locked && cnt >= 1000) {
		pll_select(CLK_24M);
	} else {
		pll_select(CLK_PLL_OUT);
	}

	switch_select(CLK_PLL_MUX);
}

int32_t pll_init(void)
{
#if defined(CONFIG_EVB_BOARD)
	//the fault value is ok for everyting, so just wait for pll locked is ok
	clk_select();
	pll_cfg_core_adc_div();
	//global_reset();
#endif
	return 0;
}

clk_freq get_mod_clk_rate(enum crg_mod mod)
{
	uint32_t i;
	uint32_t size = ARRAY_SIZE(g_sys_crg_mod_clk_freq);

	for (i = 0; i < size; i++) {
		if (mod == MOD_CLK_FREQ_PTR(i)->mod)
			return MOD_CLK_FREQ_PTR(i)->freq;
	}

	return DEF_MOD_CLK_FREQ;
}

